/*
 * License GNU LGPL
 * Copyright (C) 2012 Amrullah <amrullah@panemu.com>.
 */
package com.abc.cheque.ui.table;

import com.sun.javafx.css.StyleManager;
import com.sun.javafx.scene.control.skin.TableRowSkin;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableRow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;

/**
 *
 * @author Amrullah <amrullah@panemu.com>
 */
public class TableRowControl<T> extends TableRow<T> {

    private boolean selected;
    private TableControl<T> tblView;

    public TableRowControl(final TableControl tblView) {
        super();
        this.tblView = tblView;
        attachScrollListener();
        this.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @SuppressWarnings("unchecked")
			@Override
            public void handle(MouseEvent event) {
                if (event.getButton().equals(MouseButton.PRIMARY)
                        && event.getClickCount() == 2
                        && TableRowControl.this.getIndex() < tblView.getRecords().size()) {
                    tblView.getController().doubleClick(tblView.getSelectionModel().getSelectedItem());
                }
            }
        });

        tblView.modeProperty().addListener(new WeakChangeListener<>(modeChangeListener));
        tblView.getSelectionModel().selectedIndexProperty().addListener(new WeakChangeListener<>(selectionChangeListener));
        tblView.editingCellProperty().addListener(new WeakChangeListener<>(editingCellListener));
        tblView.agileEditingProperty().addListener(new WeakChangeListener<>(agileChangeListener));
    }

    /**
     * Handle row index change caused by scrolling the table. If the table is
     * scrolled, the same TableRow object is reused but the position and the
     * item ara changed. This listener change the display content of the row to
     * prevent displaying cell editor controls in the wrong index.
     */
    private void attachScrollListener() {
        this.indexProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
                if (tblView.getMode() == TableControl.Mode.READ || !tblView.isAgileEditing()) {
                    return;
                }
                /**
                 * The previously selected row might have different row Index
                 * after it is scrolled back.
                 */
                if (selected) {
                    selected = newValue.intValue() == tblView.getSelectionModel().getSelectedIndex();
                    setCellContentDisplay(selected ? ContentDisplay.GRAPHIC_ONLY : ContentDisplay.TEXT_ONLY);
                } else if (newValue.intValue() == tblView.getSelectionModel().getSelectedIndex()) {
                    selected = true;
                    setCellContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                }
            }
        });
    }

    private void setCellContentDisplay(ContentDisplay contentDisplay) {

        TableRowSkin skin = (TableRowSkin) getSkin();
        if (skin == null) {
            return;
        }
        for (Node node : skin.getChildren()) {
            TableCell cell = (TableCell) node;
            if (cell.getTableColumn().isEditable()) {
                cell.setContentDisplay(contentDisplay);
            }
        }
    }

    public void refreshLookupSiblings(String propertyName) {

        TableRowSkin skin = (TableRowSkin) getSkin();
        for (Node node : skin.getChildren()) {
            TableCell cell = (TableCell) node;
            if (cell.getTableColumn() instanceof BaseColumn) {
                BaseColumn baseColumn = (BaseColumn) cell.getTableColumn();
                if (baseColumn.getPropertyName().startsWith(propertyName) && !baseColumn.getPropertyName().equals(propertyName)) {
                    ObservableValue currentObservableValue = baseColumn.getCellObservableValue(cell.getIndex());
                    ((BaseCell) cell).updateItem(currentObservableValue.getValue(), currentObservableValue == null);
                }
            }
        }
    }

    @Override
    public long impl_getPseudoClassState() {
        long mask = super.impl_getPseudoClassState();
        if (tblView.getChangedRecords().contains(getItem())) {
            mask |= StyleManager.getInstance().getPseudoclassMask("changed");
        }
        return mask;
    }
    /////////////////////////////////////////////////////////
    // LISTENERS
    /////////////////////////////////////////////////////////
    /**
     * If in normalEditing mode (agileEditing == false), when a particular cell
     * is no longer being edited, the content display should change to TEXT_ONLY
     */
    private ChangeListener<TablePosition<T, ?>> editingCellListener = new ChangeListener<TablePosition<T, ?>>() {
        @Override
        public void changed(ObservableValue<? extends TablePosition<T, ?>> observable, TablePosition<T, ?> oldValue, TablePosition<T, ?> newValue) {
            if ((newValue == null || newValue.getRow() == -1) && !tblView.isAgileEditing()) {
                setCellContentDisplay(ContentDisplay.TEXT_ONLY);
            }
        }
    };
    /**
     * Display cell editors in selected row. This listener is only relevant in
     * agileEditing mode
     */
    private ChangeListener<Number> selectionChangeListener = new ChangeListener<Number>() {
        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
            if (!tblView.isAgileEditing()) {
                return;
            }
            if (tblView.getTableView().isEditable()) {
                if (t.equals(getIndex()) && selected) {
                    setCellContentDisplay(ContentDisplay.TEXT_ONLY);
                    selected = false;
                } else if (t1.equals(getIndex()) && !selected) {
                    setCellContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                    selected = true;
                }
            }
        }
    };
    /**
     * Set content display to TEXT_ONLY in READ mode. This listener is relevant
     * only in agileEditing mode
     */
    private ChangeListener<TableControl.Mode> modeChangeListener = new ChangeListener<TableControl.Mode>() {
        @Override
        public void changed(ObservableValue<? extends TableControl.Mode> observable, TableControl.Mode oldValue, TableControl.Mode newValue) {
            if (newValue == TableControl.Mode.READ && selected) {
                selected = false;
                setCellContentDisplay(ContentDisplay.TEXT_ONLY);
            }
        }
    };
    private final ChangeListener<Boolean> agileChangeListener = new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
            if (tblView.modeProperty().get() == TableControl.Mode.READ) {
                return;
            }
            if (tblView.getSelectionModel().getSelectedIndex() == getIndex()) {
                if (newValue) {
                    selected = true;
                    setCellContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                } else {
                    selected = false;
                    setCellContentDisplay(ContentDisplay.TEXT_ONLY);
                }
            }
        }
    };
}
